# -*- coding: utf-8 -*-
"""
2.6 智能体（Agents）：决策与执行 - Python 代码示例

本示例将演示如何创建一个简单的 LangChain Agent (使用 OpenAI Tools Agent 类型)：
1. 定义一个自定义工具 (使用 @tool 装饰器)。
2. 准备 LLM (支持工具调用的 ChatOpenAI 模型)。
3. 从 LangChain Hub 加载 Agent 的提示模板。
4. 使用 `create_openai_tools_agent` 创建 Agent 逻辑。
5. 创建 `AgentExecutor` 来运行 Agent。
6. 调用 AgentExecutor 并观察其思考和工具使用过程。

运行前准备：
1. 确保已安装 langchain, langchain-openai, python-dotenv, langchainhub:
   `pip install langchain langchain-openai python-dotenv langchainhub`
2. 确保项目根目录下或当前目录有 `.env` 文件，并包含 `OPENAI_API_KEY`。
3. 激活 Python 虚拟环境。
4. 运行: `python 2.6_Agents.py`
"""
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, AIMessage

# Agent 创建函数和执行器
from langchain.agents import create_openai_tools_agent, AgentExecutor

# 从 LangChain Hub 加载提示
from langchain import hub

# 加载环境变量
load_dotenv()
print("环境变量已加载。")

# --- 1. 定义自定义工具 --- 
print("\n--- 1. 定义自定义工具 --- ")

@tool
def get_word_length(word: str) -> int:
    """计算并返回一个单词的字母数量。"""
    print(f"[工具日志] 调用 get_word_length，输入: '{word}'")
    return len(word)

# 将工具放入列表
tools = [get_word_length]
print(f"工具已定义: {tools[0].name} - {tools[0].description}")

# --- 2. 准备 LLM --- 
print("\n--- 2. 准备 LLM --- ")
# 需要使用支持工具调用 (Tool Calling) 或函数调用 (Function Calling) 的模型
# 例如 gpt-3.5-turbo, gpt-4, gpt-4-turbo 等
llm = ChatOpenAI(
    model="deepseek-chat",
    openai_api_key=os.getenv("OPENAI_API_KEY"),
    openai_api_base="https://api.deepseek.com"
)
print(f"LLM ({llm.model_name}) 初始化成功。")

# --- 3. 获取 Agent 提示模板 --- 
print("\n--- 3. 获取 Agent 提示模板 --- ")
# 从 LangChain Hub 拉取预置的适用于 OpenAI Tools Agent 的提示
# 你也可以访问 https://smith.langchain.com/hub/hwchase17/openai-tools-agent 查看提示内容
prompt = hub.pull("hwchase17/openai-tools-agent")
print(f"已从 LangChain Hub 加载提示: {prompt.messages}")

# 检查提示模板中的输入变量
print(f"提示模板输入变量: {prompt.input_variables}") 
# 通常包含 'agent_scratchpad', 'chat_history', 'input', 'tool_names', 'tools'

# (可选) 如果你需要添加聊天历史支持，可以修改提示
# prompt.input_variables.append("chat_history")
# prompt.messages.insert(1, MessagesPlaceholder(variable_name="chat_history"))

# --- 4. 创建 Agent 逻辑 --- 
print("\n--- 4. 创建 Agent 逻辑 --- ")
# 使用工厂函数创建 Agent (这是一个 Runnable)
agent = create_openai_tools_agent(llm, tools, prompt)
print("Agent 逻辑创建成功。")

# --- 5. 创建 Agent 执行器 --- 
print("\n--- 5. 创建 Agent 执行器 --- ")
# AgentExecutor 负责运行 Agent 的思考-行动循环
agent_executor = AgentExecutor(
    agent=agent, 
    tools=tools, 
    verbose=True, # 设置为 True 可以看到 Agent 的思考过程、工具调用和结果
    # memory=... # 如果需要对话记忆，可以在这里添加 Memory 对象
    handle_parsing_errors=True # 尝试处理解析错误
)
print("AgentExecutor 创建成功。")

# --- 6. 运行 Agent --- 
print("\n--- 6. 运行 Agent --- ")

# 第一次调用：简单问题，不需要工具
print("\n调用 1: 简单问候")
input1 = "你好吗？"
response1 = agent_executor.invoke({"input": input1})
print(f"\n输入 1: {input1}")
print(f"Agent 输出 1: {response1['output']}")

# 第二次调用：需要使用工具
print("\n\n调用 2: 需要使用工具")
input2 = "单词 'LangChain' 有多少个字母？"
response2 = agent_executor.invoke({"input": input2})
print(f"\n输入 2: {input2}")
print(f"Agent 输出 2: {response2['output']}") # 应该调用 get_word_length 工具

# (可选) 演示包含聊天历史的调用 (如果修改了提示并添加了 memory)
# print("\n\n调用 3: 包含聊天历史")
# chat_history = [
#     HumanMessage(content=input1),
#     AIMessage(content=response1['output']),
#     HumanMessage(content=input2),
#     AIMessage(content=response2['output'])
# ]
# input3 = "我问的第二个问题是什么？"
# response3 = agent_executor.invoke({"input": input3, "chat_history": chat_history})
# print(f"\n输入 3: {input3}")
# print(f"Agent 输出 3: {response3['output']}")

print("\nAgent 示例运行完毕。")
